package fr.neyb.bernouilli.common.math;

import fr.neyb.bernouilli.common.tools.BinaryTools;

public class Fraction {
	private long numerator;
	private long denominator;

	public Fraction(long numerator, long denominator) {
		this.numerator = numerator;
		this.denominator = denominator;
		simplify();
	}

	public Fraction(long l) {
		this(l, 1);
	}

	public Fraction(Fraction f) {
		this(f.numerator, f.denominator);
	}

	public Fraction multiply(Fraction multiplicator) {
		numerator = MyMath.checkedMult(numerator, multiplicator.numerator);
		denominator = MyMath
				.checkedMult(denominator, multiplicator.denominator);

		simplify();
		return this;
	}

	public Fraction divide(Fraction divisor) {
		multiply(new Fraction(divisor).invert());
		return this;
	}

	public Fraction add(long l) {
		numerator += l * denominator;
		return this;
	}

	public Fraction add(Fraction r) {
		if (r.denominator == denominator) {
			numerator += r.numerator;
			simplify();
			return this;
		} else {
			long den = MyMath.PPCM(r.denominator, denominator);
			numerator = numerator * (den / denominator) + r.numerator
					* (den / r.denominator);

			denominator = den;
			simplify();
			return this;
		}
	}

	public Fraction invert() {
		long tmp = denominator;
		denominator = numerator;
		numerator = tmp;
		return this;
	}

	public Fraction approximate() {
		int dec = Math.min(BinaryTools.binaryDigitsNum(numerator),
				BinaryTools.binaryDigitsNum(denominator)) /2;

		numerator >>= dec;
		denominator >>= dec;

		return this;
	}

	private Fraction simplify() {
		long pgcd = MyMath.PGCD(numerator, denominator);
		numerator /= pgcd;
		denominator /= pgcd;
		return this;
	}

	@Override
	public String toString() {
		return numerator + "/" + denominator;
	}

	public double toDouble() {
		return (double) numerator / (double) denominator;
	}
}
